package phototransfer.model.filetransfer;

import android.util.Log;
import java.io.Closeable;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class FileReceiveBuffer extends Thread implements Closeable {
   public static final String TAG = "FileReceiveBuffer";
   private static final int MAX_PACKET_SIZE = 2000;
   private AckSender ackSender;
   private DatagramSocket socket;
   private DatagramPacket udpPacket;
   private boolean finishedReceiving;
   private ArrayList<DataPacket> buffer;
   private Lock lock;
   private Condition nextPacketAvailable;
   private int lastConsecutiveSeqNo;
   private int nextPacketSeqNo;
   private static final int BUFFER_SIZE = 1000;

   public FileReceiveBuffer(int port) throws SocketException {
      this.socket = new DatagramSocket(port);
      this.udpPacket = new DatagramPacket(new byte[2000], 2000);
      this.buffer = new ArrayList();
      this.lock = new ReentrantLock();
      this.nextPacketAvailable = this.lock.newCondition();
      this.finishedReceiving = false;
      this.init();
   }

   public void init() {
      this.buffer.clear();
      this.ackSender = null;
      this.nextPacketSeqNo = 0;
      this.lastConsecutiveSeqNo = -1;
   }

   public void close() {
      this.socket.close();
   }

   public void run() {
      while(!this.finishedReceiving) {
         Arrays.fill(this.udpPacket.getData(), (byte)0);

         try {
            this.socket.receive(this.udpPacket);
         } catch (IOException var6) {
            continue;
         }

         DataPacket packet = new DataPacket(this.udpPacket.getData());
         if (packet != null && !packet.isCorrupt()) {
            if (this.ackSender == null && packet.isInitPacket()) {
               int ackPort = packet.getAckPort();
               InetAddress dest = this.udpPacket.getAddress();

               try {
                  this.ackSender = new AckSender(dest, ackPort);
               } catch (SocketException var5) {
                  continue;
               }
            }

            this.updateBuffer(packet);
            if (this.ackSender != null) {
               this.ackSender.sendAck(this.lastConsecutiveSeqNo);
            }
         } else {
            Log.d("FileReceiveBuffer", "[recv corrupt packet]");
         }
      }

   }

   public DataPacket getNextPacket() {
      boolean packetFound = false;
      DataPacket packet = null;
      this.lock.lock();

      try {
         while(!packetFound) {
            Iterator iter = this.buffer.iterator();

            while(iter.hasNext()) {
               packet = (DataPacket)iter.next();
               if (packet.getSequenceNumber() == this.nextPacketSeqNo) {
                  iter.remove();
                  packetFound = true;
                  break;
               }
            }

            if (!packetFound) {
               try {
                  this.nextPacketAvailable.await();
               } catch (InterruptedException var8) {
               }
            }
         }

         ++this.nextPacketSeqNo;
      } finally {
         this.lock.unlock();
      }

      return packet;
   }

   private void updateBuffer(DataPacket packet) {
      int SEGMENT_SIZE = true;
      int sequenceNumber = packet.getSequenceNumber();
      String start;
      if (packet.isInitPacket()) {
         start = "start";
      } else if (packet.isLastPacket()) {
         start = "end";
      } else {
         start = Integer.toString((sequenceNumber - 1) * 1000);
      }

      int length = packet.getData().length;
      this.lock.lock();

      try {
         if (!this.packetIsInBufferWindow(sequenceNumber)) {
            Log.d("FileReceiveBuffer", String.format("[recv data] %s (%d) IGNORED", start, length));
            return;
         }

         this.save(packet);
         if (sequenceNumber == this.nextPacketSeqNo) {
            Log.d("FileReceiveBuffer", String.format("[recv data] %s (%d) ACCEPTED(in-order)", start, length));
            this.nextPacketAvailable.signal();
         } else {
            Log.d("FileReceiveBuffer", String.format("[recv data] %s (%d) ACCEPTED(out-of-order)", start, length));
         }

         if (sequenceNumber == this.lastConsecutiveSeqNo + 1) {
            this.updateLatestSequenceNumber();
         }
      } finally {
         this.lock.unlock();
      }

   }

   private boolean packetIsInBufferWindow(int sequenceNumber) {
      if (sequenceNumber >= this.nextPacketSeqNo && sequenceNumber < this.nextPacketSeqNo + 1000) {
         Iterator<DataPacket> iter = this.buffer.iterator();
         DataPacket packet = null;

         do {
            if (!iter.hasNext()) {
               return true;
            }

            packet = (DataPacket)iter.next();
         } while(packet.getSequenceNumber() != sequenceNumber);

         return false;
      } else {
         return false;
      }
   }

   private void save(DataPacket packet) {
      this.buffer.add(packet);
   }

   private void updateLatestSequenceNumber() {
      int i;
      do {
         for(i = 0; i < this.buffer.size(); ++i) {
            DataPacket packet = (DataPacket)this.buffer.get(i);
            if (packet.getSequenceNumber() == this.lastConsecutiveSeqNo + 1) {
               ++this.lastConsecutiveSeqNo;
               break;
            }
         }
      } while(i != this.buffer.size());

   }

   public void sendLastAck(int count) {
      for(int i = 0; i < count; ++i) {
         this.ackSender.sendAck(this.lastConsecutiveSeqNo);
      }

   }

   public void stopListening() {
      this.finishedReceiving = true;
      this.socket.close();
   }
}
